# 编程来源于生活
# 1、什么是函数？ *****
# 在程序中,函数就具备某一功能的工具事先将工具准备好   锤子  扳手   工厂
# 即函数的定义遇到应用场景拿来就用即函数的调用所以务必记住：
# 函数的使用必须遵循先定义,后调用的原则（先制造了工具，工具才能使用）
# 2、为何要用函数 不用函数产生问题是：
# 1、程序冗长
#
# 2 程序的扩展性差
#
# 3 程序的可读性差
# 3.如何用函数:
# 1、函数定义阶段: *****
# 只检测函数体的语法( 工厂合不合格)，不执行函数体代码 （不使用工厂）
# def 函数名+括号+冒号
# # # 缩进+体代码
# def factory(): # 制造一个工厂
# #     '''
# #     这个是工厂
# #     :return:
# #     '''
# #     print('正在制造手机') # 代码相当于员工和机器
# #     print('正在制造手机') # 代码相当于员工和机器
# #     print('正在制造手机') # 代码相当于员工和机器
# #     print('正在制造手机') # 代码相当于员工和机器
# # # # # 2、函数调用阶段: *****
# # # # # 函数的调用：函数名加括号
# # # # # 1 先找到名字   (找到工厂的位置)
# # # # # 2 根据名字调用代码   ( 加了括号执行工厂进行加工)
# # factory()
# # # 函数的内存地址
# # print(factory)


# 函数的三大特性
# 功能，参数，返回值
#### 有代码(有功能)
# def factory():   #  制造一个工厂
# 	print('正在制造手机')  # 代码相对于员工或者机器
# factory()
#### 无代码（用来测试，先做好项目的需求）
# def shopping():
# #     pass
# # shopping()

# 参数
# 无参函数
# 应用场景仅仅只是执行一些操作，比如与用户交互，打印（工厂制造手机，不需要外接资源）
# def factory():   #  制造一个工厂
# 	print('正在制造手机')  # 代码相对于员工或者机器
# factory()
# 有参
# 需要根据外部传进来的参数，才能执行相应的逻辑，计算 （工厂制造手机，需要a零件和b零件）
# def factory(a,b):   #  制造一个工厂
#     # 1 形参: 指的是在定义函数阶段括号内指定变量名,即形参本质就是"变量名"
#     #  形参可以看做一个变量名
#     print('a是%s'%a)
#     print('b是%s'%b)

#2 实参: 指的是在调用函数阶段括号内传入的值,即实参本质就是"值"
# 调用函数的参数称为实参
# 形参与实参的关系:在调用函数时,会将实参(值)赋值(绑定)给形参(变量名)
# factory(3,2)
# 注意:但凡按照位置定义的形参,必须被实参传值,多一个不行,少一个也不行
# 位置参数 *****
# 错误示范
# factory(1,2,3)
# # factory(1)
#关键字实参: *****
# 在调用函数阶段,按照key=value的形式指名道姓地为形参传值
#注意:
#1. 可以完全打乱顺序,但仍然能指名道姓为指定的形参传值
#2. 可以混合使用位置实参与关键字实参,但是必须注意:
#    2.1 位置实参必须放到关键字实参前面
#    2.2 不能对一个形参重复赋值
# factory(b=1,a=2)
# 错误示范
# factory(a =1 ,a=2,b=2)
# factory(1,a=1,b=2)
# factory(a=2,2)
# 正确
# factory(2,b=2)

#默认参数  *****
#默认参数:指的是在定义函数阶段,就已经为某个形参赋值了,改形参称之为有默认值的形参,简称默认形参
#注意:
#1. 在定义阶段就已经被赋值,意味着在调用阶段可以不用为其赋值
#2. 位置形参应该放到默认形参前面
#3. 默认参数的值在函数定义阶段就已经固定死了
# def factory(a,b=2):#  在定义阶段就已经被赋值，意味着在调用可以不用为其赋值,也可以对其重新赋值
#     c = a + b
#     print(c)
#
# factory(1,5)
# 位置形参vs默认形参
# 对于大多情况下传的值都不相同的,应该定义成位置形参
# 对于大多情况下传的值都相同的,应该定义成默认形参

def register(name,age,sex='男'):
    print(name,age,sex)

# register('dahai',18)
# register('xishi',18,'女')
# register('xialuo',18)
# register('dahai',18)
# register('dahai',18)

#  可变长度的参数(不定长参数) ****
# 1. 在形参中带*:会将调用函数时溢出位置实参保存成元组的形式,然后赋值*后的变量名
# def foo(x,y,*z):
#     print(x,y,z)
#     print(*z)
# foo(1,2,3,4,5,6)

# def foo(x,y,z):
#     print(x,y,z)
# # 实参打散
# foo(1,*(2,3))
# foo(*(1,2,3))
# foo(*[1,2,3])
# foo(*'abc')
# 3. 在形参中带**:会将调用函数时溢出关键字实参保存成字典的形式,然后赋值**后的变量名
def foo(x,y,**z):
    print(x,y,z)

foo(1,2,a=1,b=2,c=3)
foo(1,y=1,a=1,b=2,c=3)
#5. 规范: 在形参中带*与**的,*后的变量名应该为args,**后跟的变量名应该时kwargs

# def factory(*args,**kwargs):# * 接受的是0个到多个位置参数
#     # ** 接受的是0个到多个关键字参数
#     print(args)
#     print(kwargs)
# factory(1,2,3,x=3,y=4)
# factory(*[1,2,3],**{'x':3,'y':4})
# !!!!!!!!!!!!!!!当我们想要将传给一个函数的参数格式原方不动地转嫁给其内部的一个函数,
# 应该使用下面这种形式
# def bar(x,y,z):
# #     print(x,y,z)
# # def wrapper(*args,**kwargs):
# #     bar(*args,**kwargs)
# # wrapper(1,2,3)
# 虽然调用的是wrapper,但是要遵循的确是bar定义的参数标准
# 了解
# 命名关键字参数: 放到*与**之间的参数称之为命名关键字参数 ***
def foo(x,y,*args,m,n,**kwargs):
    print(x,y) # 1 2
    print(args) # (3, 4, 5, 6)放到元组
    print(m,n) # 3 4
    print(kwargs) # {'a': 1, 'b': 2, 'c': 3}
# # 错误
# # foo(1,2,3,4,5,6,a=1,b=2,c=3)
# foo(1,2,3,4,5,6,m=3,n=4,a=1,b=2,c=3)

